﻿// Copyright (c) 2012, Alexander Endris
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#region Usings

using System;
using System.Drawing;

#endregion

namespace NamelessOne.Imaging.Jpg
{
  /// <summary>
  ///   Represents a color value in Luma and Chroma values
  /// </summary>
  public struct YCbCr
  {
    #region Public / Attributes

    public byte Cb;
    public byte Cr;
    public byte Y;

    #endregion

    #region Public / Constructors

    public YCbCr(byte y, byte cb, byte cr)
    {
      Y = y;
      Cb = cb;
      Cr = cr;
    }

    #endregion

    #region Public / Methods

    /// <summary>
    ///   Convert from RGB to YCbCR
    /// </summary>
    public static implicit operator YCbCr(Color c)
    {
      YCbCr result = new YCbCr();

      float red = c.R/255f;
      float green = c.G/255f;
      float blue = c.B/255f;

      float y = (0.299f*red + 0.587f*green + 0.114f*blue);
      float cb = (-0.168736f*red - 0.331264f*green + 0.5f*blue);
      float cr = (0.5f*red - 0.418688f*green - 0.081312f*blue);

      result.Y = (byte) Math.Min(y*256, 255);
      result.Cb = (byte) Math.Min(cb*256 + 128, 255);
      result.Cr = (byte) Math.Min(cr*256 + 128, 255);

      return result;
    }

    /// <summary>
    ///   Converts from YCbCr to RGB
    /// </summary>
    public static implicit operator Color(YCbCr c)
    {
      float y = c.Y/255f;
      float cb = c.Cb/255f - 0.5f;
      float cr = c.Cr/255f - 0.5f;

      float r = (y + 1.402f*cr);
      float g = (y - 0.34414f*cb - 0.71414f*cr);
      float b = (y + 1.772f*cb);

      byte red = (byte) Math.Max(0, Math.Min(r*256, 255));
      byte green = (byte) Math.Max(0, Math.Min(g*256, 255));
      byte blue = (byte) Math.Max(0, Math.Min(b*256, 255));

      return Color.FromArgb(red, green, blue);
    }

    public override string ToString()
    {
      return string.Format("Y: {0}; Cb: {1}; Cr: {2}", Y, Cb, Cr);
    }

    #endregion
  }
}